package com.jinmdz.fmis.api.wrapper;

import RY3jni.CRY3;
import com.jinmdz.fmis.api.model.rockey.KeyContent;
import com.jinmdz.fmis.api.model.rockey.KeyResult;
import com.jinmdz.fmis.api.model.rockey.RockeyInfo;
import com.jinmdz.fmis.common.util.DataUtil;
import com.jinmdz.fmis.common.util.DateUtil;
import com.jinmdz.fmis.core.license.CertificateExtraModel;
import com.jinmdz.fmis.core.util.JacksonUtil;
import com.jinmdz.fmis.core.util.LogUtil;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.zip.GZIPInputStream;

/**
 * 加密狗封装处理类
 *
 * @author LiCongLu
 * @date 2020-04-16 15:24
 */
@Component
public class RockeyWrapper {

    /**
     * 最大开发授权日期
     */
    public static final int MaximumDevelopmentAuthorizationDate = 20;

    /**
     * 最大临时授权日期
     */
    public static final int MaximumProvisionalAuthorizationDate = 10;

    /**
     * 软件注册Licence信息最大长度，长度越大操作加密狗越慢，最大不能超过8192字节
     */
    public static final int MaximumSoftLicenceLength = 1024;

    /**
     * VendorID 长度
     */
    public static final int VENDOR_ID_LENGTH = 8;
    /**
     * DeveloperPIN 长度
     */
    public static final int DEVELOPER_PIN_LENGTH = 24;

    /**
     * 读取加密狗信息
     *
     * @param vendorId     加密狗VendorID
     * @param developerPin 加密狗DeveloperPIN
     * @return
     * @author LiCongLu
     * @date 2020-04-16 15:34
     */
    private RockeyInfo readRockey(String vendorId, String developerPin) {

        // 返回信息
        RockeyInfo rockeyInfo = new RockeyInfo();

        // 判断请求参数长度
        if (vendorId.length() != VENDOR_ID_LENGTH || developerPin.length() != DEVELOPER_PIN_LENGTH) {
            return rockeyInfo;
        }

        try {
            //  创建引用类
            CRY3 cry3 = new CRY3();
            //8,转化VendorID
            char[] vendorIdChars = new char[16];
            for (int i = 0; i < VENDOR_ID_LENGTH; i++) {
                vendorIdChars[i] = vendorId.charAt(i);
            }

            //24,转化DeveloperPIN
            char[] developerPinChars = new char[30];
            for (int j = 0; j < DEVELOPER_PIN_LENGTH; j++) {
                developerPinChars[j] = developerPin.charAt(j);
            }

            // 查找加密狗
            int[] findCount = new int[4];
            cry3.RY3_Find(vendorIdChars, findCount);

            // 判断是否查找到加密狗,官方所给的demo里用此条件判断
            if (findCount[0] != 0) {
                // 打开加密狗
                cry3.RY3_Open(1);
                // 验证DeveloperPIN
                int[] verifyDevPinCount = new int[4];
                cry3.RY3_VerifyDevPin(developerPinChars, verifyDevPinCount);

                //16 读取HardID
                char[] hardIdChars = new char[32];
                cry3.RY3_GetHardID(hardIdChars);
                // 记录HardID
                String hardId = String.valueOf(hardIdChars);
                // 去除结尾\u0000
                hardId = hardId.replace("\u0000", "");
                rockeyInfo.setHardId(hardId);

                // 读取加密狗内容，即秘钥
                byte[] readBytes = new byte[MaximumSoftLicenceLength];
                cry3.RY3_Read(0, readBytes, readBytes.length);
                // 记录秘钥信息
                String secretKey = new String(readBytes, "UTF-8");
                // 去除结尾\u0000
                secretKey = secretKey.replace("\u0000", "");
                rockeyInfo.setSecretKey(secretKey);

                // 关闭加密狗
                cry3.RY3_Close(true);

                // 设置成功状态
                rockeyInfo.setAsSuccess(1);
            }
        } catch (Exception e) {
            LogUtil.line("读取加密狗异常：" + e.getMessage());
        }
        // 返回异常信息
        return rockeyInfo;
    }

    /**
     * 使用gzip进行解压缩
     *
     * @param
     * @return
     * @author LiCongLu
     * @date 2020-04-16 13:21
     */
    private String decompress(byte[] bytes) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        GZIPInputStream ginZip = null;
        String decompressed = null;
        try {
            in = new ByteArrayInputStream(bytes);
            ginZip = new GZIPInputStream(in);

            byte[] buffer = new byte[1024];
            int offset = -1;
            while ((offset = ginZip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            decompressed = out.toString();
        } catch (Exception e) {
            throw e;
        } finally {
            if (ginZip != null) {
                try {
                    ginZip.close();
                } catch (IOException e) {
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                }
            }
        }

        return decompressed;
    }

    /**
     * 将16进制字符串转字节数组
     *
     * @param hexString 16进制字符串
     * @return
     * @author LiCongLu
     * @date 2020-04-16 13:22
     */
    private byte[] strToHexByte(String hexString) throws Exception {
        hexString = hexString.replace(" ", "");
        if ((hexString.length() % 2) != 0) {
            hexString += " ";
        }
        byte[] returnBytes = new byte[hexString.length() / 2];
        for (int i = 0; i < returnBytes.length; i++) {
            String inHex = hexString.substring(i * 2, i * 2 + 2);
            returnBytes[i] = (byte) Integer.parseInt(inHex, 16);
        }
        return returnBytes;
    }

    /**
     * 验证加密狗结果信息
     *
     * @param extraModel 扩展信息
     * @param nowDate    当前日期
     * @return
     * @author LiCongLu
     * @date 2020-04-16 16:44
     */
    public KeyResult checkSoftLicense(CertificateExtraModel extraModel, Date nowDate) {
        KeyResult result = new KeyResult().setSuccess(false);
        try {
            String secretKey = extraModel.getSecretKey();
            // 识别秘钥内容
            KeyContent content = JacksonUtil.json2Obj(decompress(strToHexByte(extraModel.getSecretKey())), KeyContent.class);
            switch (content.AuthorizationType) {
                case 0:
                    // 用户试用版授权
                case 1:
                    // 用户正式版
                    checkSoftLicense(extraModel, result, secretKey, content, nowDate);
                    break;
                case 2:
                    // 开发授权版
                    result.setData(content);
                    checkLicenseDueDate(result, "开发授权", MaximumDevelopmentAuthorizationDate, nowDate);
                    break;
                case 3:
                    // 临时授权版
                    result.setData(content);
                    checkLicenseDueDate(result, "临时授权", MaximumProvisionalAuthorizationDate, nowDate);
                    break;
                default:
                    result.setMessage("无效授权码，授权码类型错误！");
                    break;

            }
        } catch (Exception e) {
            result.setMessage("授权验证失败");
        }

        return result;
    }

    /**
     * 检查软件序列号信息是否正确
     *
     * @param extraModel 扩展信息
     * @param result     返回结果
     * @param secretKey  秘钥
     * @param content    秘钥解析内容
     * @return
     * @author LiCongLu
     * @date 2020-04-17 13:06
     */
    private KeyResult checkSoftLicense(CertificateExtraModel extraModel, KeyResult result, String secretKey, KeyContent content, Date nowDate) {
        try {
            // 验证硬件连接信息
            if (DataUtil.invalid(extraModel.getVendorId(), extraModel.getDeveloperPin(), extraModel.getSecretKey())) {
                return result.setMessage("加密狗验证信息无效");
            }

            // 读取解密狗信息
            RockeyInfo rockeyInfo = readRockey(extraModel.getVendorId(), extraModel.getDeveloperPin());

            // 验证读取加密狗信息
            if (DataUtil.isNull(rockeyInfo) || DataUtil.invalid(rockeyInfo.getHardId(), rockeyInfo.getSecretKey())) {
                return result.setMessage("服务器硬件Key无法访问！");
            }

            //1、验证硬件和秘钥一致性
            if (!DataUtil.equals(secretKey, rockeyInfo.getSecretKey())) {
                return result.setMessage("软件授权码错误！");
            }
            // 验证授权码
            if (!DataUtil.equals(rockeyInfo.getHardId(), content.USBKeyNumber)) {
                return result.setMessage("软件授权信息与硬件Key不匹配！");
            }

            result.setData(content);

            // 0 如果是试用版授权，需要检查软件试用期，正式版授权不许要检查
            if (content.AuthorizationType == 0) {
                return checkLicenseDueDate(result, "试用期授权", -1, nowDate);
            }

            // 1正式版授权
            if (content.AuthorizationType == 1) {
                return result.setSuccess(true)
                        .setMessage("用户正式版授权软件");
            }
        } catch (Exception e) {
            result.setMessage("服务器硬件Key无法访问！");
        }
        return result;
    }

    /**
     * 检查软件授权期限
     *
     * @param result                   返回结果
     * @param authorizationTypeName    授权类型
     * @param maximumAuthorizationDate 允许的最大授权天数
     * @param nowDate                  当前日期
     * @return
     * @author LiCongLu
     * @date 2020-04-17 13:06
     */
    private KeyResult checkLicenseDueDate(KeyResult result, String authorizationTypeName, int maximumAuthorizationDate, Date nowDate) {
        result.setSuccess(false);
        if (DataUtil.isNull(result.getData())) {
            return result.setSuccess(false).setMessage("未检测到软件授权信息");
        }
        // 截止日期
        Date dueDate = DateUtil.parsePattern(result.getData().DueDate, "yyyy-MM-dd");
        long hours = (dueDate.getTime() - nowDate.getTime()) / (1000 * 60 * 60);
        long totalDays = hours / 24;
        if (totalDays < 0) {
            result.setMessage("【" + authorizationTypeName + "】截止到【" + result.getData().DueDate + "】，时间已超期！");
        } else if (maximumAuthorizationDate != -1 && totalDays > maximumAuthorizationDate) {
            result.setMessage("无效【" + authorizationTypeName + "】码，授权时间超出最大允许时间！");
        } else {
            result.setSuccess(true);
            result.setMessage("软件【" + authorizationTypeName + "】剩余【" + totalDays + "】天");
        }
        return result;
    }
}
